package com.gitee.fivefaith.hikpreview.utils;

import com.gitee.fivefaith.hikpreview.component.hik.HCNetSDK;
import com.gitee.fivefaith.hikpreview.component.hik.HCNetSDK.FRealDataCallBack_V30;
import com.gitee.fivefaith.hikpreview.component.hik.HCNetSDK.NET_DVR_PREVIEWINFO;
import com.gitee.fivefaith.hikpreview.component.hik.common.OsSelect;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.concurrent.CompletableFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;

/**
 * @author zhaoanchi
 * @version 1.0
 * @since 1.0
 */
@SuppressWarnings("AlibabaLowerCamelCaseVariableNaming")
@Slf4j
public class HikTool {

    private static HCNetSDK hCNetSDK = null;

    private String baseSrsRtmp = "rtmp://127.0.0.1:1935/live/";
    private String ip = "192.168.1.200";
    private short port = 8000;
    private String user = "admin";
    private String psw = "password";

    static {
        synchronized (HCNetSDK.class) {
            String strDllPath = "";
            try {
                // win系统加载库路径
                if (OsSelect.isWindows()) {
                    strDllPath =
                        System.getProperty("user.dir") + "\\src\\main\\resources\\lib\\window64\\HCNetSDK" + ".dll";
                }
                // Linux系统加载库路径
                else if (OsSelect.isLinux()) {
                    strDllPath = System.getProperty("user.dir") + "/src/main/resources/lib/linux64/libhcnetsdk.so";
                }
                hCNetSDK = (HCNetSDK)Native.loadLibrary(strDllPath, HCNetSDK.class);
            } catch (Exception ex) {
                log.error("loadLibrary: {}", strDllPath, ex);
            }
        }
    }

    public Pair<Integer, Integer> start() throws IOException {
        hCNetSDK.NET_DVR_Init();
        // 登录
        int userId = loginDevice(ip, port, user, psw);
        if (userId < 0) {
            throw new RuntimeException("login fail");
        }

        // 2. 创建管道
        PipedOutputStream pos = new PipedOutputStream();
        PipedInputStream pis = new PipedInputStream(pos);

        // 3. 启动 FFmpeg 推流
        CompletableFuture.runAsync(() -> pushWithFFmpeg(pis, baseSrsRtmp + userId));

        // 4. 开始取流
        NET_DVR_PREVIEWINFO previewInfo = new NET_DVR_PREVIEWINFO();
        // 通道号，根据实际设备通道情况设置, 可以配置多个摄像头
        previewInfo.lChannel = 1;
        // 流类型，0 为子码流，1 为主码流等
        previewInfo.dwStreamType = 0;
        // 连接方式，0 为 TCP，1 为 UDP 等
        previewInfo.dwLinkMode = 0;
        // 是否阻塞模式，1 为阻塞，0 为非阻塞
        previewInfo.bBlocked = 1;

        FRealDataCallBack_V30 realDataCallBack =
            (int lRealHandle, int dwDataType, Pointer pBuffer, int dwBufSize, Pointer pUser) -> {
                if (dwDataType == HCNetSDK.NET_DVR_STREAMDATA) {
                    byte[] buf = pBuffer.getByteArray(0, dwBufSize);
                    try {
                        pos.write(buf);
                    } catch (IOException ignore) {
                    }
                }
            };
        int playHandle = hCNetSDK.NET_DVR_RealPlay_V40(userId, previewInfo, realDataCallBack, null);
        if (playHandle == -1) {
            log.error("获取实时视频流失败，错误码：{}", hCNetSDK.NET_DVR_GetLastError());
            hCNetSDK.NET_DVR_Logout(userId);
            hCNetSDK.NET_DVR_Cleanup();
            return Pair.of(userId, -1);
        }
        return Pair.of(userId, playHandle);
    }

    public void stop(int userId, int playHandle) {
        // 停止预览并清理资源
        hCNetSDK.NET_DVR_StopRealPlay(playHandle);
        hCNetSDK.NET_DVR_Logout(userId);
        hCNetSDK.NET_DVR_Cleanup();
    }

    private void pushWithFFmpeg(InputStream in, String srsRtmpUrl) {
        try {
            ProcessBuilder pb = new ProcessBuilder("ffmpeg",
                // 从 stdin 读 H264
                "-re", "-i", "pipe:0", "-vcodec", "copy", "-f", "flv", srsRtmpUrl);
            pb.redirectErrorStream(true);
            Process proc = pb.start();
            try (OutputStream ffmpegIn = proc.getOutputStream()) {
                // 海康线程写 → FFmpeg读
                in.transferTo(ffmpegIn);
            }
            proc.waitFor();
        } catch (Exception e) {
            log.error("ffmpeg error", e);
        }
    }

    /**
     * 登录设备，支持 V40 和 V30 版本，功能一致。
     *
     * @param ip 设备IP地址
     * @param port SDK端口，默认为设备的8000端口
     * @param user 设备用户名
     * @param psw 设备密码
     * @return 登录成功返回用户ID，失败返回-1
     */
    public static int loginDevice(String ip, short port, String user, String psw) {
        // 创建设备登录信息和设备信息对象
        HCNetSDK.NET_DVR_USER_LOGIN_INFO loginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO();
        HCNetSDK.NET_DVR_DEVICEINFO_V40 deviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();

        // 设置设备IP地址
        byte[] deviceAddress = new byte[HCNetSDK.NET_DVR_DEV_ADDRESS_MAX_LEN];
        byte[] ipBytes = ip.getBytes();
        System.arraycopy(ipBytes, 0, deviceAddress, 0, Math.min(ipBytes.length, deviceAddress.length));
        loginInfo.sDeviceAddress = deviceAddress;

        // 设置用户名和密码
        byte[] userName = new byte[HCNetSDK.NET_DVR_LOGIN_USERNAME_MAX_LEN];
        byte[] password = psw.getBytes();
        System.arraycopy(user.getBytes(), 0, userName, 0, Math.min(user.length(), userName.length));
        System.arraycopy(password, 0, loginInfo.sPassword, 0, Math.min(password.length, loginInfo.sPassword.length));
        loginInfo.sUserName = userName;

        // 设置端口和登录模式
        loginInfo.wPort = port;
        // 同步登录
        loginInfo.bUseAsynLogin = false;
        // 使用SDK私有协议
        loginInfo.byLoginMode = 0;

        // 执行登录操作
        int userID = hCNetSDK.NET_DVR_Login_V40(loginInfo, deviceInfo);
        if (userID == -1) {
            log.error("登录失败，错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
        } else {
            log.info("{} 设备登录成功！", ip);
            // 处理通道号逻辑
            int startDChan = deviceInfo.struDeviceV30.byStartDChan;
            log.info("预览起始通道号: {}", startDChan);
        }
        // 返回登录结果
        return userID;
    }

}
